package org.wheel.plugins.toolkit.util.sql.visitor;

import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import lombok.Getter;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.statement.select.SelectItem;
import org.wheel.module.core.request.ParamField;
import org.wheel.plugins.toolkit.sql.enums.SqlModel;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * SQL参数替换访问者
 * 用于遍历和替换SQL表达式中的命名参数
 */
public class SqlParameterReplacementVisitor extends ExpressionVisitorAdapter<Void> {

    // 直接使用 LogFactory 创建日志对象，Hutool 会自动检测项目中的日志框架（优先级：Slf4j > Log4j > Logback > JDK Logging）
    private static final Log log = LogFactory.get();

    private final Map<String, ParamField> fieldToCondition;
    private final SqlModel sqlModel;
    private final AtomicInteger parameterIndex;
    private final Set<String> targetParameters;

    // 最终返回的参数列表
    @Getter
    private final LinkedHashMap<String, Object> parameterMaps;

    // 当前的字段名
    private String currentColumnName;
    // 当前左侧或右侧的表达式
    private Expression currentLeftOrRightExpression;
    // 当前的完整的表达式
    private Expression currentExpression;
    // 当前的表达式是否是右侧的表达式: true表示右侧，false表示左侧
    private boolean right;

    public SqlParameterReplacementVisitor(Map<String, ParamField> fieldToCondition,
                                        SqlModel sqlModel,
                                        Set<String> targetParameters) {
        this.fieldToCondition = fieldToCondition;
        this.sqlModel = sqlModel;
        this.parameterIndex = new AtomicInteger(1);
        this.targetParameters = targetParameters;
        this.parameterMaps = new LinkedHashMap<>();
    }


    @Override
    public <S> Void visit(JdbcNamedParameter parameter, S context) {
        String paramName = parameter.getName();

        // 检查这个参数是否在目标参数列表中
        if (targetParameters.contains(paramName)) {
            // 检查是否有对应的条件
            ParamField condition = fieldToCondition.get(paramName);
            if (condition != null) {
                // 根据SQL模型类型替换参数
                // 在Visitor模式中，实际的替换逻辑在父表达式的visit方法中处理
                log.debug("替换参数：{}, currentColumnName: {}, currentExpression: {}, currentLeftOrRightExpression: {}, is right Expression: {}", paramName, currentColumnName, currentExpression, this.currentLeftOrRightExpression, right);

                LinkedHashMap<String, Object> tmp = new LinkedHashMap<>();
                tmp.put(condition.getField(), condition.getValue());
                SqlParameterReplacementHandler.paramMapHandle(parameterMaps, tmp, sqlModel, parameterIndex.get(), false);

                Expression replacement = SqlParameterReplacementHandler.convertToPlaceholderExpression(fieldToCondition.get(paramName).getValue(), sqlModel, paramName, parameterIndex);
                if(this.right){
                    /*
                     * 1、继承 BinaryExpression 包括：
                     *     ComparisonOperator 比较类型，LikeExpression 匹配类型，AndExpression 逻辑运算符，OrExpression 逻辑运算符
                     * 2、继承 ASTNodeAccessImpl implements Expression 包括：
                     *     InExpression 匹配类型，Between between 匹配类型，Function 函数类型，CaseExpression 匹配类型，WhenClause 匹配类型，ExistsExpression 匹配类型
                     */
                    if(currentExpression instanceof BinaryExpression binaryExpression){
                        if(currentLeftOrRightExpression instanceof ParenthesedExpressionList){
                            ((ParenthesedExpressionList)currentLeftOrRightExpression).withExpressions(replacement);
                        }else {
                            binaryExpression.withRightExpression(replacement);
                        }
                    } else if(currentExpression instanceof InExpression inExpression){
                        inExpression.withRightExpression(new ParenthesedExpressionList<>(replacement));
                    } else if(currentExpression instanceof Between between){
                    } else if(currentExpression instanceof IntervalExpression intervalExpression){
                        intervalExpression.setExpression(replacement);
                    }
                }else{
                    if(currentExpression instanceof BinaryExpression binaryExpression){
                        if(currentLeftOrRightExpression instanceof ParenthesedExpressionList){
                            ((ParenthesedExpressionList)currentLeftOrRightExpression).withExpressions(replacement);
                        }else {
                            binaryExpression.withLeftExpression(replacement);
                        }
                    } else if(currentExpression instanceof InExpression inExpression){
                        inExpression.withLeftExpression(new ParenthesedExpressionList<>(replacement));
                    } else if(currentExpression instanceof Between between){
                    }
                }
            }
        }
        return null;
    }

    @Override
    public <S> Void visit(JdbcParameter parameter, S context) {
        // JdbcParameter是叶子节点，不需要进一步遍历
        return null;
    }

//    public <S> Void visit(DollarParameter parameter, S context) {
//        // DollarParameter是叶子节点，不需要进一步遍历
//        return null;
//    }
//
//    public <S> Void visit(MyBatisParameter parameter, S context) {
//        // MyBatisParameter是叶子节点，不需要进一步遍历
//        return null;
//    }

    @Override
    public <S> Void visit(SelectItem<? extends Expression> selectItem, S context) {
        this.currentExpression = selectItem.getExpression();
        this.right = true;
        selectItem.getExpression().accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(EqualsTo equalsTo, S context) {
//        equalsTo.getLeftExpression().accept(this, context);
//        equalsTo.getRightExpression().accept(this, context);
        this.currentExpression = equalsTo;
        Expression leftExpr = equalsTo.getLeftExpression();
        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        // 检查右侧表达式是否是JdbcNamedParameter并且需要替换
        Expression rightExpr = equalsTo.getRightExpression();
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(NotEqualsTo notEqualsTo, S context) {
//        notEqualsTo.getLeftExpression().accept(this, context);
//        notEqualsTo.getRightExpression().accept(this, context);
        this.currentExpression = notEqualsTo;
        Expression leftExpr = notEqualsTo.getLeftExpression();
        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        Expression rightExpr = notEqualsTo.getRightExpression();
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(GreaterThan greaterThan, S context) {
//        greaterThan.getLeftExpression().accept(this, context);
//        greaterThan.getRightExpression().accept(this, context);
        this.currentExpression = greaterThan;
        Expression leftExpr = greaterThan.getLeftExpression();
        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        Expression rightExpr = greaterThan.getRightExpression();
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(GreaterThanEquals greaterThanEquals, S context) {
//        greaterThanEquals.getLeftExpression().accept(this, context);
//        greaterThanEquals.getRightExpression().accept(this, context);
        this.currentExpression = greaterThanEquals;
        Expression leftExpr = greaterThanEquals.getLeftExpression();
        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        Expression rightExpr = greaterThanEquals.getRightExpression();
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(MinorThan minorThan, S context) {
//        minorThan.getLeftExpression().accept(this, context);
//        minorThan.getRightExpression().accept(this, context);
        this.currentExpression = minorThan;
        Expression leftExpr = minorThan.getLeftExpression();
        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        Expression rightExpr = minorThan.getRightExpression();
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(MinorThanEquals minorThanEquals, S context) {
//        minorThanEquals.getLeftExpression().accept(this, context);
//        minorThanEquals.getRightExpression().accept(this, context);
        this.currentExpression = minorThanEquals;
        Expression leftExpr = minorThanEquals.getLeftExpression();
        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        Expression rightExpr = minorThanEquals.getRightExpression();
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(LikeExpression likeExpression, S context) {
//        likeExpression.getLeftExpression().accept(this, context);
//        likeExpression.getRightExpression().accept(this, context);
        this.currentExpression = likeExpression;
        Expression leftExpr = likeExpression.getLeftExpression();
        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        Expression rightExpr = likeExpression.getRightExpression();
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(InExpression inExpression, S context) {
        this.currentExpression = inExpression;
        Expression left = inExpression.getLeftExpression();
        log.debug("inExpression left: " + left);
        this.currentLeftOrRightExpression = left;
        this.right = false;
        left.accept(this, context);

        Expression rightExpr = inExpression.getRightExpression();
        log.debug("inExpression right: " + rightExpr);
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(Between between, S context) {
        this.currentExpression = between;
//        between.getLeftExpression().accept(this, context);
//        between.getBetweenExpressionStart().accept(this, context);
//        between.getBetweenExpressionEnd().accept(this, context);
        Expression leftExpr = between.getLeftExpression();
        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        // 处理开始表达式
        Expression startExpr = between.getBetweenExpressionStart();
        this.currentLeftOrRightExpression = startExpr;
        String startParamName = needReplace(startExpr);
        if (StrUtil.isNotBlank(startParamName)) {
            Expression replacement = SqlParameterReplacementHandler.convertToPlaceholderExpression(
                    fieldToCondition.get(startParamName).getValue(), sqlModel, startParamName, parameterIndex);
            between.setBetweenExpressionStart(replacement);
        } else {
            startExpr.accept(this, context);
        }

        // 处理结束表达式
        Expression endExpr = between.getBetweenExpressionEnd();
        this.currentLeftOrRightExpression = endExpr;
        String endParamName = needReplace(endExpr);
        if (StrUtil.isNotBlank(endParamName)) {
            Expression replacement = SqlParameterReplacementHandler.convertToPlaceholderExpression(
                    fieldToCondition.get(endParamName).getValue(), sqlModel, endParamName, parameterIndex);
            between.setBetweenExpressionEnd(replacement);
        } else {
            endExpr.accept(this, context);
        }
        return null;
    }

    @Override
    public <S> Void visit(AndExpression andExpression, S context) {
//        andExpression.getLeftExpression().accept(this, context);
//        andExpression.getRightExpression().accept(this, context);
        this.currentExpression = andExpression;
        Expression leftExpr = andExpression.getLeftExpression();

        this.currentLeftOrRightExpression = leftExpr;
        this.right = false;
        leftExpr.accept(this, context);

        Expression rightExpr = andExpression.getRightExpression();
        this.currentLeftOrRightExpression = rightExpr;
        this.right = true;
        rightExpr.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(OrExpression orExpression, S context) {
        this.currentExpression = orExpression;
        Expression left = orExpression.getLeftExpression();
        this.currentLeftOrRightExpression = left;
        this.right = false;
        left.accept(this, context);

        Expression right = orExpression.getRightExpression();
        this.currentLeftOrRightExpression = right;
        this.right = true;
        right.accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(Column column, S context) {
        // Column是叶子节点，不需要进一步遍历
        this.currentColumnName = column.getColumnName();
        return null;
    }

    @Override
    public <S> Void visit(LongValue longValue, S context) {
        // LongValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(DoubleValue doubleValue, S context) {
        // DoubleValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(StringValue stringValue, S context) {
        // StringValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(DateValue dateValue, S context) {
        // DateValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(TimestampValue timestampValue, S context) {
        // TimestampValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(NullValue nullValue, S context) {
        // NullValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(Function function, S context) {
        this.currentExpression = function;
        this.right = true;
        log.debug("Found Function: " + function.getName());
        // 可能是查询函数，也可能是在group 或者having中
        // 处理函数表达式中的参数
        if (function.getParameters() != null) {
            function.getParameters().accept(this, context);
        }else if(function.getNamedParameters() != null){
            function.getNamedParameters().accept(this, context);
        }
        return null;
    }

    @Override
    public <S> Void visit(IntervalExpression intervalExpression, S context) {
        this.currentExpression = intervalExpression;
        this.right = true;
        log.debug("Found IntervalExpression: " + intervalExpression.getExpression());
        if(intervalExpression.getExpression() != null) {
            intervalExpression.getExpression().accept(this, context);
        }

        return null;
    }

    @Override
    public <S> Void visit(CaseExpression caseExpression, S context) {
        // 处理CASE表达式
        if (caseExpression.getSwitchExpression() != null) {
            caseExpression.getSwitchExpression().accept(this, context);
        }

        if (caseExpression.getWhenClauses() != null) {
            for (WhenClause whenClause : caseExpression.getWhenClauses()) {
                whenClause.accept(this, context);
            }
        }

        if (caseExpression.getElseExpression() != null) {
            caseExpression.getElseExpression().accept(this, context);
        }
        return null;
    }

    @Override
    public <S> Void visit(WhenClause whenClause, S context) {
        whenClause.getWhenExpression().accept(this, context);
        whenClause.getThenExpression().accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(ExistsExpression existsExpression, S context) {
        Expression right = existsExpression.getRightExpression();
        if (right instanceof ParenthesedSelect) {
            ((ParenthesedSelect) right).getPlainSelect().getWhere().accept(this, context);
        } else {
            existsExpression.getRightExpression().accept(this, context);
        }
        return null;
    }


    @Override
    public <S> Void visit(ExpressionList<?> expressionList, S context) {
        for (Expression expr : expressionList) {
            expr.accept(this, context);
        }
        return null;
    }


    /**
     * 判断是否需要替换, 如果是则返回参数名, 否则返回null
     * @param expression 表达式
     * @return 参数名, 如果需要替换则返回参数名, 否则返回null
     */
    private String needReplace(Expression expression) {
        if (expression instanceof JdbcNamedParameter) {
            String tmp = ((JdbcNamedParameter) expression).getName();
            if(targetParameters.contains(tmp)){
                return tmp;
            }
        }
        return null;
    }
}
